/*
 * Copyright 2019 Web3 Labs Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
 * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations under the License.
 */
package org.web3j.crypto;

import java.io.IOException;
import java.math.BigInteger;
import java.security.InvalidAlgorithmParameterException;
import java.security.KeyPair;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.util.Arrays;

import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.ec.ECPair;
import org.bouncycastle.crypto.params.ParametersWithRandom;
import org.bouncycastle.crypto.signers.SM2Signer;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.util.encoders.Hex;
import org.junit.jupiter.api.Test;

import org.web3j.crypto.Sign.SignatureData;
import org.web3j.crypto.gm.SM2Signature;
import org.web3j.crypto.gm.StandardDSAEncoding;
import org.web3j.utils.Numeric;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

public class ECRecoverTest {

	public static final String PERSONAL_MESSAGE_PREFIX = "\u0019Ethereum Signed Message:\n";

	public static final StandardDSAEncoding STANDARD_DSA_ENCODING = new StandardDSAEncoding();

	public static void testRecoverAddressFromSignature() throws IOException {
//    	
//    	EC Public Key [79:d5:7a:54:1a:cb:76:76:23:d0:bb:83:3c:51:4a:24:fa:ec:e9:54]
//                X: a8d99be904e014bfcbdf17300d132c0b7fae5ef7600ae4cdd8ff2a10dfb2c176
//                Y: 7f68ae83c7b55237f668a6cc8131723853a07a9cfd2cf6c259c2b9c316d63cae
//
//    EC Private Key [79:d5:7a:54:1a:cb:76:76:23:d0:bb:83:3c:51:4a:24:fa:ec:e9:54]
//                X: a8d99be904e014bfcbdf17300d132c0b7fae5ef7600ae4cdd8ff2a10dfb2c176
//                Y: 7f68ae83c7b55237f668a6cc8131723853a07a9cfd2cf6c259c2b9c316d63cae
//
//    3067022025902dfea1a55674a8e7570bbb999174d92114ab50b3648c28589bef00597f8702210082785b23b181fe8a250de937298c039b51c48cde34daadf3e5694ca91bcd2a7c022044bfb87d11b3792ae6f07518aa4259be6335c7db6a06b2651413c003687e57e7
//    f5b89099663da2f8b10f313408cbeb094cfeb42a
//    8843393912553760753813763521264989899264486576001122634529776213263534587619703931189642095538237619275342891871546549295952151885207277650931780928552110

		String signature = "0x3067022025902dfea1a55674a8e7570bbb999174d92114ab50b3648c28589bef00597f8702210082785b23b181fe8a250de937298c039b51c48cde34daadf3e5694ca91bcd2a7c022044bfb87d11b3792ae6f07518aa4259be6335c7db6a06b2651413c003687e57e7";

		String address = "0xf5b89099663da2f8b10f313408cbeb094cfeb42a";
		String message = "adopapa";

		byte[] signatureBytes = Numeric.hexStringToByteArray(signature);
		System.out.println(signatureBytes.length);

		BigInteger[] decoded = STANDARD_DSA_ENCODING.decode(Sign.CURVE.getN(), signatureBytes);
		System.out.println(decoded[0].toByteArray().length);
		System.out.println(decoded[1].toByteArray().length);
		System.out.println(decoded[2].toByteArray().length);

//        SignatureData sd =
//                new SignatureData(
//                		(byte[]) Arrays.copyOfRange(signatureBytes, 64, 96),
//                        (byte[]) Arrays.copyOfRange(signatureBytes, 0, 32),
//                        (byte[]) Arrays.copyOfRange(signatureBytes, 32, 64));

		SignatureData sd = new SignatureData(decoded[2].toByteArray(), //
				decoded[0].toByteArray(), decoded[1].toByteArray());

		String addressRecovered = null;
		boolean match = false;

		// Iterate for each possible key to recover
		BigInteger publicKey = Sign.recoverFromSignature(new SM2Signature(new BigInteger(1, sd.getR()),
				new BigInteger(1, sd.getS()), new BigInteger(1, sd.getV())), message.getBytes());

		System.out.println(publicKey);

		if (publicKey != null) {
			addressRecovered = "0x" + Keys.getAddress(publicKey);
			System.out.println("0-------------------------------------------");
			System.out.println(addressRecovered);
		}

//        assertEquals(addressRecovered, (address));
//        assertTrue(match);
	}
//    @Test
//    public void testRecoverAddressFromSignature() {
//    	
//    	String signature =
//    			"0x2c6401216c9031b9a6fb8cbfccab4fcec6c951cdf40e2320108d1856eb532250576865fbcd452bcdc4c57321b619ed7a9cfd38bd973c3e1e0243ac2777fe9d5b1b";
//    	
//    	String address = "0x31b26e43651e9371c88af3d36c14cfd938baf4fd";
//    	String message = "v0G9u7huK4mJb2K1";
//    	
//    	String prefix = PERSONAL_MESSAGE_PREFIX + message.length();
//    	byte[] msgHash = Hash.sha3((prefix + message).getBytes());
//    	
//    	byte[] signatureBytes = Numeric.hexStringToByteArray(signature);
//    	byte v = signatureBytes[64];
//    	if (v < 27) {
//    		v += 27;
//    	}
//    	
//    	SignatureData sd =
//    			new SignatureData(
//    					v,
//    					(byte[]) Arrays.copyOfRange(signatureBytes, 0, 32),
//    					(byte[]) Arrays.copyOfRange(signatureBytes, 32, 64));
//    	
//    	String addressRecovered = null;
//    	boolean match = false;
//    	
//    	// Iterate for each possible key to recover
//    	for (int i = 0; i < 4; i++) {
//    		BigInteger publicKey =
//    				Sign.recoverFromSignature(
//    						(byte) i,
//    						new ECDSASignature(
//    								new BigInteger(1, sd.getR()), new BigInteger(1, sd.getS())),
//    						msgHash);
//    		
//    		if (publicKey != null) {
//    			addressRecovered = "0x" + Keys.getAddress(publicKey);
//    			
//    			if (addressRecovered.equals(address)) {
//    				match = true;
//    				break;
//    			}
//    		}
//    	}
//    	
//    	assertEquals(addressRecovered, (address));
//    	assertTrue(match);
//    }

	public static void testSign() throws Exception {
		KeyPair keyPair = Keys.createSm2p256v1KeyPair();
		System.out.println(keyPair.getPublic());
		System.out.println(keyPair.getPrivate());

		ECKeyPair ecKeyPair = ECKeyPair.create(keyPair);
		SM2Signature sm2Sign = ecKeyPair.sm2Signature("adopapa".getBytes());
		BigInteger pubKey = Sign.recoverFromSignature(sm2Sign, "adopapa".getBytes());
		System.err.println(ecKeyPair.getPublicKey());
		System.err.println(pubKey);

		System.err.println(sm2Sign.r.toByteArray().length);
		System.err.println(sm2Sign.s.toByteArray().length);
		System.err.println(sm2Sign.v.toByteArray().length);

		byte[] signdata = ecKeyPair.sm2Signdata("adopapa".getBytes());
		System.err.println(signdata.length);
		System.out.println(Hex.toHexString(signdata));
		System.out.println(Keys.getAddress(ecKeyPair));
//    	BCECPublicKey publicKey = (BCECPublicKey) keyPair.getPublic();
//    	System.err.println("******************************");
//    	System.err.println(publicKey.getQ().getAffineXCoord().toBigInteger());
//    	System.err.println(publicKey.getQ().getAffineYCoord().toBigInteger());
		System.out.println(ecKeyPair.sm2Verifydata(signdata, "adopapa".getBytes()));

	}

	public static void testkey() throws Exception {
		for (int i = 0; i < 100; i++) {
			KeyPair keyPair = Keys.createSm2p256v1KeyPair();
			System.out.println(keyPair.getPublic());
			System.out.println(keyPair.getPrivate());

			ECKeyPair ecKeyPair = ECKeyPair.create(keyPair);
			System.out.println(ecKeyPair.getPublicKey());
			BCECPublicKey publicKey = (BCECPublicKey) keyPair.getPublic();
			System.out.println(Hex.toHexString(publicKey.getQ().getEncoded(false)));
//        	System.out.println(ecKeyPair.getPublicKey().toByteArray().length);
			System.out.println(Hex.toHexString(Sign.getPublicKeyBytes(ecKeyPair.getPublicKey())));

		}
	}

	public static void main(String[] args) throws Exception {
//		testSign();
		testRecoverAddressFromSignature();
//    	testkey();
	}

}
